#ifndef VECT_H
#define VECT_H

#include <math.h>
#include <assert.h>
#include <string>
#include <sstream>
using namespace std;


/*Class that encapsulates 3 dimensional vector*/
template< class T > 
class Vect
{
 private:
         
         static const int dim;
         T e[ 3 + 1 ];                                                        //ignoring entry 0
    
 public:
        
        Vect( void )
        { }
        
        Vect( T e1, T e2, T e3 )
        {
              e[ 1 ] = e1;
              e[ 2 ] = e2;
              e[ 3 ] = e3;      
        }   

		//Konstruktor kopiujacy
		Vect( const Vect< T >& V )
		{
              e[ 1 ] = V.e[1];
              e[ 2 ] = V.e[2];
              e[ 3 ] = V.e[3];      
		}

        //this should be overloaded [] operator - who cares
        void setEntry( int pos, T value )
        {
			  assert( pos >= 1 && pos <= 3 && "Vect (Vect.h) subscript not beetween 1 - 3" );
              e[ pos ] = value;     
        }

		//to samo co wyzej krotsza nazwa
		void set( int pos, T value )
		{
			 assert( pos >= 1 && pos <= 3 && "Vect (Vect.h) subscript not beetween 1 - 3" );
			 e[ pos ] = value; 
		}
        
        //this also should be overloaded [] operator - i don't give a damn yeahh
        T getEntry( int pos ) const
        {
			 assert( pos >= 1 && pos <= 3 && "Vect (Vect.h) subscript not beetween 1 - 3" );
             return e[ pos ];     
        }

		//to samo co wyzej 
		T get( int pos ) const
		{
			 assert( pos >= 1 && pos <= 3 && "Vect (Vect.h) subscript not beetween 1 - 3" );
			 return e[ pos ];
		}

		//Przeladownie operatora * na uzytek mnozenia przez skalar
		Vect< T > operator*( T a ) const
		{
			Vect< T > v;

			v.setEntry( 1, e[1] * a );
			v.setEntry( 2, e[2] * a );
			v.setEntry( 3, e[3] * a );

			return v;
		}
        
		//Operator przypisania
		void operator=( const Vect< T > v )
		{
			e[1] = v.getEntry(1);
			e[2] = v.getEntry(2);
			e[3] = v.getEntry(3);
		}

		Vect< T > operator-( const Vect< T > v )
		{
			Vect< T > r;

			r.e[1] = e[1] - v.e[1];
			r.e[2] = e[2] - v.e[2];
			r.e[3] = e[3] - v.e[3];

			return r;
		}

		Vect< T > operator+( const Vect< T > v )
		{
			Vect< T > r;

			r.e[1] = e[1] + v.e[1];
			r.e[2] = e[2] + v.e[2];
			r.e[3] = e[3] + v.e[3];

			return r;
		}

		Vect< T > operator-()
		{
			Vect< T > r;

			r.e[1] = -e[1];
			r.e[2] = -e[2];
			r.e[3] = -e[3];

			return r;
		}

		//! normalizuje ten wektor. 
		void normalize()
		{
			T len = sqrt( e[1]*e[1] + e[2]*e[2] + e[3]*e[3] );
			e[1] = e[1]/len;
			e[2] = e[2]/len;
			e[3] = e[3]/len;
		}

		//! Zwraca wektor w postaci stringa
		string toString();

		//! Zwraca pointer na tablice w ktore trzymane sa dane
		T* getArrayPointer()
		{
			return e;
		}

        template< class S >
        friend class Matrix; 
};

/*Wariable tells dimension of vector*/
template< class T >
const int Vect< T >::dim = 3; 


//---------------------------------- toString --------------------------------//
template< class T >
string Vect< T >::toString()
{
	stringstream  s;
	s << "|" << e[1] << "|" << endl << "|" << e[2] << "|" << endl << "|" << e[3] << "|";

	return s.str();
}

//===================== FUNCTIONS THAT WORK ON 3DIM VECTORS ==================//


//Oblicza iloczyn wektorowy - cross product
template< class S >
Vect< S > crossProduct( const Vect< S > P, const Vect< S > Q )
{
	Vect< S > R;

	R.set(1, P.get(2)*Q.get(3) - P.get(3)*Q.get(2) );
	R.set(2, P.get(3)*Q.get(1) - P.get(1)*Q.get(3) );
	R.set(3, P.get(1)*Q.get(2) - P.get(2)*Q.get(1) );

	return R;
}

//Oblicza norme wektora
template< class S > 
S norm( const Vect< S > v )
{
	return ( S ) sqrt( v.get(1)*v.get(1) + v.get(2)*v.get(2) + v.get(3)*v.get(3) );
}

//Normalizuje dany wektor
template< class S > 
void normalize( Vect< S >& P )
{
	S norma = norm( P );

	P.set( 1, P.get(1)/norma );	
	P.set( 2, P.get(2)/norma );
	P.set( 3, P.get(3)/norma );
}

//Oblicza iloczyn skalarny - dot product
template< class S > 
S dotProduct( const Vect< S > V, const Vect< S > W )
{
	return V.getEntry( 1 ) * W.getEntry( 1 ) + V.getEntry( 2 ) * W.getEntry( 2 ) + V.getEntry( 3 ) * W.getEntry( 3 );
}

/*Function that returns firs position of biggest entry in Vector. In addition, 
you can  narrow the numbers of entries taken under consideration. For example:
positionOfBiggestEntry( Vector< int >( 100, 1, 3 ), 2, 3 ) - returns 3  */
template< class S > 
int positionOfBiggestEntry( const Vect< S > V, int from, int to )
{
         if( to - from + 1 == 3 )
         {
             S max;
             int pos;
             
             if( V.getEntry( 1 ) >= V.getEntry( 2 ) )
             {
                 max = V.getEntry( 1 );
                 pos = 1;    
             }    
             else
             {
                 max = V.getEntry( 2 ) ;
                 pos = 2;   
             }
             
             if( V.getEntry( 3 ) > max )
                 pos = 3;   
                 
             return pos;
         }            
         else
         if( to - from + 1 == 2 )
         {
             if( V.getEntry( from ) >= V.getEntry( to ) )
                 return from;  

             return to;
         }           
         else
             return from;
}

/*Funkcja oblicza projekcje wektora P na wektor Q*/
template< class S > 
Vect< S > proj( const Vect< S > P, const Vect< S > Q )
{
	Vect< S > v;

	v = Q * (dotProduct( P, Q )/dotProduct( Q, Q ));

	return v;
}

#endif VECT_H